package lru

import "container/list"

type Cache struct {
	maxBytes int64
	nbytes int64
	ll *list.List
	cache map[string]*list.Element
	onEvicted func(key string,value Value)
	//OnEvicted 是某条记录被移除时的回调函数，可以为 nil
}

type Value interface {
	Len() int
}

//双向链表节点的数据类型
type entry struct {
	key string
	value Value
}

func (c *Cache) Len() int {
	return c.ll.Len()
}

//the constructor of Cache
func New(maxbytes int64,onevicted func(string,Value)) *Cache {
	return &Cache{
		maxBytes:  maxbytes,
		ll:        list.New(),
		cache:     make(map[string]*list.Element),
		onEvicted: onevicted,
	}
}

func (c *Cache) Get(key string) (value Value,ok bool) {
	if ele,ok := c.cache[key];ok{
		c.ll.MoveToFront(ele)
		kv := ele.Value.(*entry)
		//list 存储的是任意类型，使用时需要类型转换
		//interface类型转换是.(被转换类型)
		return kv.value,true
	}
	return
}

func (c *Cache) RemoveOldest()  {
	ele := c.ll.Back()
	if ele != nil {
		c.ll.Remove(ele)
		kv := ele.Value.(*entry)
		delete(c.cache,kv.key)
		c.nbytes -= int64(len(kv.key)) + int64(kv.value.Len())
		if c.onEvicted != nil {
			c.onEvicted(kv.key,kv.value)
		}
	}
}

func (c *Cache) Add(key string,value Value) {
	if ele,ok := c.cache[key]; ok {
		c.ll.MoveToFront(ele)
		kv := ele.Value.(*entry)
		c.nbytes += int64(value.Len()) - int64(kv.value.Len())
		//老的值被更新，但是没有从nbytes里面减去，只是被新的value覆盖了。所以只要加上差值即可。
	} else {
		ele := c.ll.PushFront(&entry{key: key,value: value})
		c.cache[key] = ele
		c.nbytes += int64(len(key)) + int64(value.Len())
	}
	for c.maxBytes != 0 && c.maxBytes < c.nbytes {
		c.RemoveOldest()
	}
}